Github issueをアサインしたらコード修正&PR出すAI Agentをつくる
Introduction
先日Devinが発表されました。
課題を与えると、自律的に情報収集・コーディング・デバッグ等
システム構築をやってくれるすごいAIだそうです。
ここまでやらなくても、Agent用アカウントにGihubでissueをassignしたら
自動で実装してpull requestだしてくれたら便利では、と思い
実装してみました。
今回はAmazon Bedrock(Claude3)をつかって実装してみたので
それについて解説します。
Environment
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 14.3.1
- Node : v20.8.1
- Git : 2.43.2
AWSアカウントはセットアップ済みとします。
Setup
Agent用Githubアカウントの準備
適当なメールアドレスを用意し、Githubアカウントを作成しましょう。
アカウント作成後、Access Tokenを発行します。
ここでトークンを発行しましょう。
基本的にはFine-grained personal access tokensでいいかと思うのですが、
リポジトリによってはPersonal access tokens (classic)でないとダメかもしれません。
私の環境ではFine-grainedのトークンだとPR作成時にエラーになってしまったので、
classicのトークンを使いました。
Amazon Bedrockの準備
BedrockでClaude 3を使うので、このへんを参考にモデルを設定します。
AgentProgram
では作成していきます。
概要
ざっくりとした動きはこんな感じです。
NodeでAgentを起動した後ポーリングで自分宛てのopenなissueを取得し、
必要に応じてリポジトリのclone/updateを行います。
issue用ブランチを作成してBedrockにissue内容を問い合わせ、
結果をパースしてcommit→push→pull requestとやっていきます。
使用したライブラリなど
実装はNodeでtypescriptを使って実装いました。
GithubへのアクセスはOctakitを使い、
ローカルでのGit操作はsimple-gitを使います。
あとはaws-sdkでBedrockにアクセスしています。
各実装ポイント
Githubからissueリストをもってくる処理は下記のような感じです。
Octokitで簡単にできます。
let octokit: Octokit = new Octokit({ auth: "your githubToken" }); const response = await this.octokit.rest.issues.listForAuthenticatedUser({ filter: 'assigned', state: 'open', }); console.dir(response.data);
simple-gitを使ったGitのcloneは下記。
こちらも簡単です。
import simpleGit, { SimpleGit, SimpleGitOptions } from 'simple-git'; let git: SimpleGit = simpleGit({}); await git.clone("clone url", "your repo pash");
Bedrockへアクセス
AWS SDKでのBedrockアクセスもここのような感じで実装します。
モデルはClaude 3 Sonnetを指定します。
const client = new BedrockRuntimeClient({ region: "us-east-1" }); const modelId = "anthropic.claude-3-sonnet-20240229-v1:0"; const payload = { "anthropic_version": "bedrock-2023-05-31", "max_tokens": 1024,//必要に応じてトークン数を調整 "messages": [ { "role": "user", "content": [ { "type": "text", "text": "your prompt" } ] } ], "temperature": 0.5, "top_p": 0.9 }; const command = new InvokeModelCommand({ body: JSON.stringify(payload), contentType: "application/json", accept: "application/json", modelId, }); const response = await client.send(command); const decodedResponseBody = new TextDecoder().decode(response.body); /** @type {ResponseBody} */ const responseBody = JSON.parse(decodedResponseBody); console.dir(responseBody);
プロンプトの作り方
issue内容に対してどのように修正すればよいかをAIに返してもらいます。
プロンプトは下記のような感じでJSON SCHEMAとcodebase全体のコードを渡して
修正内容を提案してもらいました。
下記に幾つかの要求のポリシーを示します。これに従って回答してください。 - 回答は下記JSON SCHEMAに合致するJSON形式で実施してください。 ``` { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "hoge": { "type": "string" }, "fuga": { "type": "string" } } } ``` - 上記JSON SCHEMAのプロパティの意味は下記です - ここに各プロパティ内容や仕様を記述 以下の<issue/>が依頼する内容です。 <codebase/>は依頼する内容に関連するコード内容です。 -------------------------------------------------------- <codebase> //codebase全体の情報 </codebase> <issue> //issue内容 </issue>
codebase全体のコードはcode2promptで
ファイル出力したものをloadしてます。
AIからの出力はJSON SCHEMAを渡してその形式で返してもらいます。
こうしておけば自分で定義したclassにJSON.parseでそのまま変換できます。
ファイル修正〜pull request
修正すべき内容が取得できたので、それを適用していきます。
cloneしたcodebaseに対してファイルのupdateを適用していきます。
修正できたらsimple-gitでadd,commit,push。
let git: SimpleGit = simpleGit({}); await git.add("file path"); await git.commit("commitMessage"); await git.push('origin', "branch name");
pushできたらPRを作成します。
const { data } = await octokit.pulls.create({ owner:"リポジトリオーナー名", repo: "リポジトリ名", head: `Pull Requestを作成するブランチ名`, base: 'mainとかのbaseになるブランチ', title: "PR タイトル", body: "PR 内容", }); console.dir(data);
テスト用リポジトリで適当なコードをmainブランチに用意し、
コラボレータとしてAI Agentをinviteします。
そして、↓のようなissueを作成してAgentをアサインします。
作成されたPRは↓みたいな感じです。
「CSSファイル作成してhtmlに適用して」というissueを作成し
実際にマージして動作確認したら、ちゃんとCSSが適用されてました。
今回はシンプルな例でしたが、プロンプトやissue内容を調整すれば
けっこういけるはず。
Summary
今回はGithubでコラボレータとして追加されたAgentに対し、
issueをアサインしたらコード修正〜Pull Requestまで実行する処理を実装してみました。
いくつissueを同時にアサインしても、(tokenが許す限り)並行で作業できるのは便利。
また、一度作ってしまえば、モデルをバージョンアップしただけで
生成されるコードの品質が高くなり、対応できるissueの幅が広がるのもよいです。
シンプルなissueには単価が安いモデルのAgentをassignし、
複雑/重要なissueには高単価のハイスペックなモデルを、なども可能ですね。
PRをレビューしてくるAgentなどもどこかで見ましたが、
AIが生成したコードをAIがレビューして修正する、なども普通にありそうです。
ちなみに、今回はポーリングでissueチェックしましたが、
Guthubのwebhookを使えばissue登録など
任意のタイミングでリクエストをpostできるので、
それのほうがよいかもしれません。
あとはBedrockアクセス〜PR作成まで、MQとか使って処理したほうがよいかも。